package main

import (
	"bufio"
	"config-client-v1/test/serial-server/pkg"
	j_log "config-client-v1/test/tcp-client/j-log"
	"config-client-v1/utils/tools"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"golang.org/x/net/context"
	"io"
	"net"
	"runtime/debug"
	"strings"
	"time"
)

const BYTE_LENGTH = 4096

type Client struct {
	RemoteAddr   string // 客户端 IP + 端口
	Connection   net.Conn
	Reader       *bufio.Reader
	CreateTime   time.Time // 连接时间
	LastDataTime time.Time // 最近一次接收数据的时间
}

var TCPClient *Client

var addr string
var id *int

func main() {
	id = flag.Int("id", 1, "tcp服务端地址")
	addrP := flag.String("addr", "192.168.1.100:2345", "tcp服务端地址")
	jsonFile = flag.String("json_file", "frame.json", "帧格式json文件")
	flag.Parse()
	err := loadingFrameItem()
	if err != nil {
		j_log.LogError([]interface{}{"加载帧格式失败", err.Error()})
		return
	}
	addr = *addrP
	TCPClient = &Client{}
	connectServer()
}

func connectServer() {
	defer func() {
		if err := recover(); err != nil {
			j_log.LogError([]interface{}{"connectServer panic", err})
			fmt.Println(string(debug.Stack()))
		}
		j_log.LogInfo([]interface{}{"3秒后尝试重连"})
		time.Sleep(3 * time.Second)
		connectServer()
	}()
	j_log.LogInfo([]interface{}{"正在连接server: ", addr})
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		if err == io.EOF {
			j_log.LogError([]interface{}{"远程连接已关闭."})
		} else if strings.Contains(err.Error(), "No connection could be made because the target machine actively refused it") {
			j_log.LogError([]interface{}{"server连接失败, 目标计算机积极拒绝"})
		} else if strings.Contains(err.Error(), "was forcibly closed by the remote host") {
			j_log.LogError([]interface{}{"远程连接已关闭."})
		} else if err == net.ErrClosed || strings.Contains(err.Error(), "use of closed network connection") || strings.Contains(err.Error(), "read: connection reset by peer") || strings.Contains(err.Error(), "closed by the remote host") {
			j_log.LogError([]interface{}{"连接已关闭或重置"})
		} else {
			j_log.LogError([]interface{}{"连接服务端失败", err.Error()})
		}
		return
	}
	j_log.LogInfo([]interface{}{"server连接成功"})
	defer func() {
		_ = conn.Close()
	}()
	TCPClient.Connection = conn
	TCPClient.Reader = bufio.NewReader(TCPClient.Connection) // 创建一个tcp返回 reader

	ctx, cancelFunc := context.WithCancel(context.Background())
	//go heartBeatCheck(ctx)
	go testSendMessage(ctx)
	var buffer [BYTE_LENGTH]byte
	var n int
	for {
		n, err = TCPClient.Reader.Read(buffer[0:])
		if err != nil {
			if err == io.EOF {
				j_log.LogError([]interface{}{"io.EOF error ", err})
			}
			if err == net.ErrClosed || strings.Contains(err.Error(), "use of closed network connection") || strings.Contains(err.Error(), "read: connection reset by peer") || strings.Contains(err.Error(), "closed by the remote host") {
				j_log.LogError([]interface{}{err})
			} else {
				j_log.LogError([]interface{}{"Read Error", err})
			}
			cancelFunc()
			return
		}
		str := tools.ByteArrayToHexStr(buffer[0:n])
		j_log.LogInfo([]interface{}{"【recv】", TCPClient.RemoteAddr, fmt.Sprintf("len=%d,str=%s", n, str)})
		// 清空buffer
		for i := 0; i < n; i++ {
			buffer[i] = 0
		}
	}
}

func testSendMessage(ctx context.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("testSendMessage panic: ", err)
		}
	}()
	var counter uint16
	counter = 1
	for {
		select {
		case <-ctx.Done():
			j_log.LogInfo([]interface{}{"关闭了testSendMessage协程"})
			return
		default:
			data0, err0 := createFrameRawData()
			if err0 != nil {
				j_log.LogError([]interface{}{"生成数据帧失败", err0.Error()})
				return
			}

			_, err := TCPClient.Connection.Write(data0)
			if err != nil {
				j_log.LogError([]interface{}{"data0发送失败"})
				return
			}
			if counter%2 == 0 {
				time.Sleep(1 * time.Millisecond)
			}
			data1, err1 := createFrameRawData()
			if err1 != nil {
				j_log.LogError([]interface{}{"生成数据帧失败", err1.Error()})
				return
			}
			_, err = TCPClient.Connection.Write(data1)
			if err != nil {
				j_log.LogError([]interface{}{"data1发送失败"})
				return
			}
			if counter%3 == 0 {
				time.Sleep(1 * time.Millisecond)
			}

			data2, err2 := createFrameRawData()
			if err2 != nil {
				j_log.LogError([]interface{}{"生成数据帧失败", err2.Error()})
				return
			}
			_, err = TCPClient.Connection.Write(data2)
			if err != nil {
				j_log.LogError([]interface{}{"data2发送失败"})
				return
			}
			if counter%4 == 0 {
				time.Sleep(1 * time.Millisecond)
			}
			data3, err3 := createFrameRawData()
			if err3 != nil {
				j_log.LogError([]interface{}{"生成数据帧失败", err3.Error()})
				return
			}
			_, err = TCPClient.Connection.Write(data3)
			if err != nil {
				j_log.LogError([]interface{}{"data3发送失败"})
				return
			}
			fmt.Printf("已发送数据帧 %d 条\n", counter)
			counter++
			time.Sleep(5 * time.Second)
		}
	}
}

func heartBeatCheck(ctx context.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("heartbeat panic: ", err)
		}
	}()
	var counter uint16
	counter = 1
	for {
		select {
		case <-ctx.Done():
			j_log.LogInfo([]interface{}{"关闭了heartBeat协程"})
			return
		default:
			str := "5A5B4920616D20616C6976655C5D"
			b, _ := pkg.HexStrToByteArray(str)
			_, err := TCPClient.Connection.Write(b)
			if err != nil {
				j_log.LogError([]interface{}{"心跳包发送失败"})
				return
			}
			counter++
			time.Sleep(10000 * time.Second)
		}
	}
}

var jsonFile *string
var list []*pkg.FrameItemParam

func loadingFrameItem() (err error) {
	str, err := pkg.ReadFileContent(*jsonFile)
	if err != nil {
		err = errors.New(fmt.Sprintf("读取帧格式文件失败 %s\n", err))
		return
	}
	list = make([]*pkg.FrameItemParam, 0)
	err = json.Unmarshal([]byte(str), &list)
	if err != nil {
		return
	}
	if len(list) == 0 {
		err = errors.New("帧格式为空, 请重新编辑")
		return
	}
	return
}

func createFrameRawData() (data []byte, err error) {
	data = make([]byte, 0)
	uniqueKeyBoolMap := make(map[string]bool)
	for i := 0; i < len(list); i++ {
		val := list[i]
		if list[i].Start > list[i].End {
			err = errors.New("开始字节不可大于结束字节")
			return
		}
		if i > 0 && len(list) > 1 {
			if !uniqueKeyBoolMap[fmt.Sprintf("%d", val.Start)] && list[i].Start != (list[i-1].End+1) {
				err = errors.New(fmt.Sprintf("[%s] 和前面字段不连续 %d~%d", val.FieldName, val.Start, val.End))
				return
			}
		}
		l := val.End - val.Start + 1
		if val.InputMethod == 1 {
			switch val.SpecType {
			case "bool":
				if l != 1 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				data = append(data, uint8(pkg.GetRandAreaNumber(0, 1)))
			case "uint8":
				if l != 1 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				data = append(data, uint8(pkg.GetRandAreaNumber(0, 255)))
			case "int8":
				if l != 1 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				data = append(data, uint8(pkg.GetRandAreaNumber(-128, 127)))
			case "uint16":
				if l != 2 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := uint16(pkg.GetRandAreaNumber(0, 65535))
				tmpB := tools.Uint16ToBytes(tmpV, "big")
				data = append(data, tmpB[:]...)
			case "int16":
				if l != 2 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := uint16(pkg.GetRandAreaNumber(-32768, 32767))
				tmpB := tools.Uint16ToBytes(tmpV, "big")
				data = append(data, tmpB[:]...)
			case "uint32":
				if l != 4 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := uint32(pkg.GetRandAreaNumber(200000, 300000))
				tmpB := tools.Uint32ToBytes(tmpV, "big")
				data = append(data, tmpB[:]...)
			case "int32":
				if l != 4 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := int32(pkg.GetRandAreaNumber(100000, 150000))
				tmpB := tools.Uint32ToBytes(uint32(tmpV), "big")
				data = append(data, tmpB[:]...)
			case "float32":
				if l != 4 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := float64(pkg.GetRandAreaNumber(-100000, 100000)) / 100.0
				tmpB := tools.Float32ToBytes4(float32(tmpV), val.Endian)
				data = append(data, tmpB[:]...)
			default:
				err = errors.New(fmt.Sprintf("%s spec_type 参数无效", val.FieldName))
				return
			}
			continue
		}
		if val.Type == "校验" {
			if val.CheckStart > val.End {
				err = errors.New("开始字节不可大于结束字节")
				return
			}
			if val.CheckStart >= len(data) {
				err = errors.New("校验起始字节异常")
				return
			}
			if val.CheckEnd >= len(data) {
				fmt.Printf("val.CheckEnd=%d,len(data)=%d\n", val.CheckEnd, len(data))
				err = errors.New("校验结束字节异常")
				return
			}

			switch val.CheckMethod {
			case "sum":
				if l != 1 {
					err = errors.New("sum校验和最多占用一个字节")
					return
				}
				byteSum := pkg.GetBytesSum(data, val.CheckStart, val.CheckEnd)
				data = append(data, byteSum)
			case "crc16":
				if l != 2 {
					err = errors.New("crc16校验最多占用两个字节")
					return
				}
				bytes := pkg.GetCrc16Result(data[val.CheckStart : val.CheckEnd+1])
				data = append(data, bytes...)
			default:
				err = errors.New("校验方法参数无效")
				return
			}
			continue
		}
		if val.RawData == "" {
			err = errors.New("非变量且非校验的raw_data不可为空")
			return
		} else {
			b, err2 := pkg.HexStrToByteArray(val.RawData)
			if err2 != nil {
				err = errors.New(fmt.Sprintf("%s  rawData值格式错误 %s", val.FieldName, err2.Error()))
				return
			}
			if len(b) != l {
				err = errors.New(fmt.Sprintf("%s  rawData值长度与占用字节不匹配", val.FieldName))
				return
			}
			data = append(data, b...)
		}
	}
	return
}
